extends Object


class_name ShapeDraw


## 添加直线
static func line_to(node: Node, start: Vector2, direction: Vector2) -> Line2D:
    return line_to_point(node, start, start + direction)

## 添加直线
static func line_to_point(node: Node, from: Vector2, to: Vector2) -> Line2D:
    var line: Line2D = Line2D.new()
    line.add_point(from)
    line.add_point(to)
    line.width = 1
    node.add_child(line)
    return line

## 返回矩形中心点以Vector2.UP为初始方向,顺时针旋转指定角度后会产生交点的边框
static func get_rect_center_intersect_line(center_size: Vector2, angle: float, mg_le: float = 0, mg_ge: float = 0) -> Dictionary:
    var w: float = center_size.x
    var h: float = center_size.y

    var margin_le: float = mg_le if not is_zero_approx(mg_le) else atan(w / h)
    var margin_ge: float = mg_ge if not is_zero_approx(mg_ge) else atan(h / w) + 0.5 * PI

    var wrap_angle: float = wrapf(angle, 0, 2 * PI)
    var format_angle: float = wrap_angle if wrap_angle < PI else 2 * PI - wrap_angle
    # 垂直边
    if margin_le < format_angle and format_angle < margin_ge:
        # 1:右边,-1:左边
        var point_sign: int = 1 if wrap_angle > 0 and wrap_angle <= PI else -1
        var p1: Vector2 = Vector2(point_sign * w, -h)
        var p2: Vector2 = Vector2(point_sign * w, h)
        var normal: Vector2 = point_sign * Vector2.RIGHT
        return {"p1": p1, "p2": p2, "normal": normal}
    # 水平边
    # 1:底边 -1:顶边
    var point_sign: int = -1 if format_angle < 0.5 * PI else 1
    var p1: Vector2 = Vector2(-w, point_sign * h)
    var p2: Vector2 = Vector2(w, point_sign * h)
    var normal:Vector2 = point_sign * Vector2.UP
    return {"p1": p1, "p2": p2, "normal": normal}

## 返回矩形以中心点Vector2.UP为初始方向,顺时针旋转指定角度后于边框的交点
static func get_rect_center_intersect_point(center_size: Vector2, angle: float, mg_le: float = 0, mg_ge: float = 0) -> Vector2:
    var cross_data: Dictionary = get_rect_center_intersect_line(center_size, angle, mg_le, mg_ge)
    var cross_line: StraightLine = StraightLine.from_point(cross_data.p1, cross_data.p2)
    var center_up_line: StraightLine = StraightLine.from_direction(Vector2.UP.rotated(angle))
    return cross_line.get_intersect_point(center_up_line)

## 以Vector2.UP开始顺时针旋转绘制实心的圆形扇形
static func get_circle_arc_points(points_arc: PackedVector2Array, radius: float, angle_from: float, angle_to: float, step_degress: int = 1) -> void:
    # 每1度一个点
    var angle_step: float = step_degress * (PI / 180)
    var count: int = (angle_to - angle_from) / angle_step
    for i in range(count):
        var angle: float = angle_from + i * angle_step
        var dest_point: Vector2 = Vector2.UP.rotated(angle) * radius
        points_arc.push_back(dest_point)

## 以Vector2.UP开始顺时针旋转绘制实心的矩形扇形
static func get_rect_arc_points(points_arc: PackedVector2Array, center_size: Vector2, angle_from: float, angle_to: float) -> void:
    var w: float = center_size.x
    var h: float = center_size.y

    var margin_le: float = atan(w / h)
    var margin_ge: float = atan(h / w) + 0.5 * PI

    var path_angles: Array = [angle_from, angle_to, margin_le, margin_ge, margin_le + PI, margin_ge + PI]
    path_angles.sort()
    # 途径的顶点
    for angle in path_angles:
        if angle < angle_from:
            continue
        if angle > angle_to:
            break
        var cross_point: Vector2 = get_rect_center_intersect_point(center_size, angle, margin_le, margin_ge)
        points_arc.push_back(cross_point)

# 在指定大小的矩形内随机获取一个坐标点
static func get_rand_rect_point(rect_size: Vector2) -> Vector2:
    var is_x: bool = randi() % 2 == 0
    var x: float = 0
    var y: float = 0
    if is_x:
        x = randf_range(0, rect_size.x)
        y = [0, rect_size.y][randi() % 2]
    else:
        y = randf_range(0, rect_size.y)
        x = [0, rect_size.x][randi() % 2]
    return Vector2(x, y)

# 获取一组按指定步长偏移的角度值
static func get_split_angles(max_split_count: int, step_angle: float) -> Array:
    var split_angles: Array = []
    var start_angle: float = 0
    if max_split_count % 2:
        split_angles.append(0)
        start_angle = step_angle
    else:
        start_angle = step_angle / 2
    for i in range(max_split_count / 2):
        var v: float = start_angle + step_angle * i
        split_angles.append(v)
        split_angles.append(-v)
    return split_angles

# 获取圆
static func circle(radius: float = 1.0, center: Vector2 = Vector2.ZERO) -> PackedVector2Array:
    var segments: int = int(4 * floor(radius / 16.0) + 16)
    var points: Array = []
    points.resize(segments + 1)
    var segment_size: float = TAU / segments
    for i in range(segments):
        points[i] = Vector2(cos(i * segment_size) * radius + center.x, sin(i * segment_size) * radius + center.y)
    points[segments] = points[0]
    return PackedVector2Array(points)

static func rectangle(extents: Vector2) -> PackedVector2Array:
    return PackedVector2Array([
        Vector2(-extents.x, -extents.y), Vector2(extents.x, -extents.y),
        Vector2(extents.x, extents.y), Vector2(-extents.x, extents.y),
    ])

static func capsule(radius: float, height: float) -> PackedVector2Array:
    radius = clamp(radius, 0.5, INF)
    height = clamp(height, 0.0, INF)
    var hheight: float = clamp( height / 2.0, 0, INF )
    var cap_circle: PackedVector2Array = circle(radius)
    var points: Array = []
    points.resize(len(cap_circle) + 2)

    var arch_count: int = int((len(cap_circle) / 2.0) + 1)
    for i in range(arch_count):
        points[i] = cap_circle[i] + Vector2(0, hheight)

    for i in range(arch_count - 1, len(cap_circle)):
        points[i + 1] = cap_circle[i] + Vector2(0, -hheight)
    points[-1] = points[0]
    return PackedVector2Array(points)

# 获取箭头
static func arrow(length: float) -> PackedVector2Array:
    length = 1.0  if is_zero_approx(length) else clamp(abs(length), 1.0, INF) * sign(length)
    var tip: Vector2 = Vector2.DOWN * length
    var head_dw: float = clamp(length * 0.1, 1, 4)
    var head_dh: float = clamp(length * 0.2, 2, 8)
    return PackedVector2Array([
        # stem
        tip + Vector2(-0.51, -head_dh), Vector2(-0.51, 0.0), Vector2(0.51, 0.0), tip + Vector2(0.51, -head_dh),
        # head
        tip + Vector2(head_dw, -head_dh), tip, tip + Vector2(-head_dw, -head_dh),
    ])

static func concave_to_polygons(p_segments: PackedVector2Array) -> PackedVector2Array:
    if not len(p_segments):
        return PackedVector2Array()
    var result: PackedVector2Array = PackedVector2Array([p_segments[0]])
    for i in range(1, len(p_segments)):
        #segment tail
        if (i % 2):
            result.push_back(p_segments[i])
        #segment head
        else:
            if p_segments[i].distance_squared_to(p_segments[i-1]) > 0.01:
                # disjoint segment started
                result.push_back(p_segments[i])
    return result

static func segment(a: Vector2, b: Vector2) -> PackedVector2Array:
    var hthick: Vector2 = (a - b).orthogonal().normalized() * 0.51
    return PackedVector2Array([
        a + hthick, b + hthick, b - hthick, a - hthick
    ])

static func line_to_polygon(normal: Vector2, d: float) -> PackedVector2Array:
    normal = normal.normalized()
    var tangent: Vector2 = normal.orthogonal() * 2048.0
    return PackedVector2Array([
        tangent + (normal * d), -tangent + (normal * d),
        -tangent + (normal*(d - 1.01)), tangent + (normal * (d-1.01)),
    ])

static func get_shape2d_polygon(shape: Shape2D) -> PackedVector2Array:
    var polygon: PackedVector2Array = PackedVector2Array()
    if shape == null:
        pass
    elif shape is CapsuleShape2D:
        polygon = capsule(shape.radius, shape.height)
    elif shape is ConvexPolygonShape2D:
        polygon = shape.points
    elif shape is CircleShape2D:
        polygon = circle(shape.radius)
    elif shape is RectangleShape2D:
        polygon = rectangle(shape.extents)
    elif shape is ConcavePolygonShape2D:
        polygon = concave_to_polygons(shape.segments)
    elif shape is SegmentShape2D:
        polygon = segment(shape.a, shape.b)
    return polygon

# 绘制轨迹
static func draw_tail(target: Node2D, tail_line2D: Line2D, max_tail_count: int) -> void:
    var tail_count: int = tail_line2D.get_point_count()
    if tail_count > 0:
        var last_point: Vector2 = tail_line2D.points[tail_count - 1]
        if target.global_position == last_point:
            if tail_count > 1:
                tail_line2D.remove_point(0)
            return
    if tail_count > max_tail_count:
        tail_line2D.remove_point(0)
    tail_line2D.add_point(target.global_position)
